const crypto = require("crypto");
const querystring = require("querystring");

const accessKeyId = process.env["AWS_ACCESS_KEY_ID"];
const secretAccessKey = process.env["AWS_SECRET_ACCESS_KEY"];
const region = process.env["AWS_DEFAULT_REGION"] || "us-east-1";
const host = process.env["AWS_ENDPOINT_HOST"] || "s3." + region + ".amazonaws.com";
const external_host = process.env["AWS_ENDPOINT_HOST_EXTERNAL"] || host;
const scheme = process.env["AWS_ENDPOINT_SCHEME"] || "https";

const now = new Date();
const date = now.toISOString().replace(/[:-]|\.\d*/g, ""); // YYYYMMDDTHHmmssZ
const dateStamp = date.slice(0, 8); // YYYYMMDD

const emptyPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

/** Returns the Authorization header */
function s3HeaderAuthorization(r) {
  const signedHeaders = "host;x-amz-content-sha256;x-amz-date";
  const uri = _uri(r.uri);
  const queryString = _queryString(r.args);
  const headers = `host:${host}\nx-amz-content-sha256:${emptyPayloadHash}\nx-amz-date:${date}\n`;
  const signature = _sign(r.method, uri, queryString, headers, signedHeaders, emptyPayloadHash);
  return (
    "AWS4-HMAC-SHA256 " +
    `Credential=${accessKeyId}/${dateStamp}/${region}/s3/aws4_request,` +
    `SignedHeaders=${signedHeaders},` +
    `Signature=${signature}`
  );
}

/** Returns the Host header */
function s3HeaderHost(r) {
  return host;
}

/** Returns the Location header for a presigned request */
function s3HeaderLocation(r) {
  const signedHeaders = "host";
  const uri = _uri(r.uri);
  const queryString = _queryString({
    "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
    "X-Amz-Credential": `${accessKeyId}/${dateStamp}/${region}/s3/aws4_request`,
    "X-Amz-Date": date,
    "X-Amz-Expires": 300,
    "X-Amz-SignedHeaders": signedHeaders,
  });
  const headers = `host:${external_host}\n`;
  const signature = _sign(r.method, uri, queryString, headers, signedHeaders, "UNSIGNED-PAYLOAD");

  return `${scheme}://${external_host}${uri}?${queryString}&X-Amz-Signature=${signature}`;
}

/** Returns the 'x-amz-content-sha256' header */
function s3HeaderXAmzContentSha256(r) {
  return emptyPayloadHash;
}

/** Returns the 'x-amz-date' header */
function s3HeaderXAmzDate(r) {
  return date;
}

/** Returns the proxied S3 URI */
function s3Uri(r) {
  return `${scheme}://${host}${_uri(r.uri)}?${_queryString(r.args)}`;
}

function _uri(uri) {
  return uri
    .split("/")
    .map((x) => encodeURIComponent(x))
    .join("/");
}

function _queryString(args) {
  return Object.keys(args)
    .sort()
    .map((key) => key + "=" + querystring.escape(args[key]))
    .join("&");
}

function _sign(method, uri, queryString, headers, signedHeaders, payloadHash) {
  const request = `${method}\n${uri}\n${queryString}\n${headers}\n${signedHeaders}\n${payloadHash}`;
  const hash = crypto.createHash("sha256").update(request).digest("hex");
  const stringToSign = `AWS4-HMAC-SHA256\n${date}\n${dateStamp}/${region}/s3/aws4_request\n${hash}`;
  return ["AWS4" + secretAccessKey, dateStamp, region, "s3", "aws4_request", stringToSign]
    .reduce((s, d) => crypto.createHmac("sha256", s).update(d).digest())
    .toString("hex");
}

export default {
  s3HeaderAuthorization,
  s3HeaderHost,
  s3HeaderLocation,
  s3HeaderXAmzContentSha256,
  s3HeaderXAmzDate,
  s3Uri,
};
